/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.skywalking.oap.server.library.module;

import java.lang.reflect.Field;
import java.util.*;
import org.slf4j.*;

/**
 * A module definition.
 *
 * @author wu-sheng, peng-yongsheng
 */
// 《核心Module继承关系.png》
// （在 ModuleManager 中）ModuleDefine 的实现类是通过 SPI 方式被加载的。
// 定义 模块的名字、提供的服务、实例化、实例化提供者、设置配置信息、执行提供者的准备阶段。
public abstract class ModuleDefine implements ModuleProviderHolder {

    private static final Logger logger = LoggerFactory.getLogger(ModuleDefine.class);

    // 属于该Module的所有ModuleProvider对象
    private final LinkedList<ModuleProvider> loadedProviders = new LinkedList<>();

    // 当前Module的名称。每个 Module 都有一个全局唯一的名称。
    private final String name;

    public ModuleDefine(String name) {
        this.name = name;
    }

    /**
     * @return the module name
     */
    public final String name() {
        return name;
    }

    /**
     * @return the {@link Service} provided by this module.
     */
    // 这个模块提供的服务
    // 模块中的每个提供者都需要提供这些服务
    public abstract Class[] services();

    /**
     * Run the prepare stage for the module, including finding all potential providers, and asking them to prepare.
     *
     * @param moduleManager of this module
     * @param configuration of this module
     * @throws ProviderNotFoundException when even don't find a single one providers.
     */
    // 查找出配置文件中包含的所有提供者（指定模块下），并将 application.yml 中对应的配置信息设置到提供者中
    void prepare(ModuleManager moduleManager,
                 ApplicationConfiguration.ModuleConfiguration configuration,
                 ServiceLoader<ModuleProvider> moduleProviderLoader) throws ProviderNotFoundException, ServiceNotProvidedException, ModuleConfigException, ModuleStartException {
        // 查找与当前 ModuleConfiguration 对应的 ModuleProvider 实现，然后记录到 loadedProviders 集合中。
        boolean providerExist = false;
        for (ModuleProvider provider : moduleProviderLoader) {
            // 这里只关心配置文件中指定的、与当前Module相关的ModuleProvider实现类
            if (!configuration.has(provider.name())) {
                continue;
            }

            providerExist = true;
            if (provider.module().equals(getClass())) {
                // 初始化ModuleProvider对象（实例化）
                ModuleProvider newProvider;
                try {
                    newProvider = provider.getClass().newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    throw new ProviderNotFoundException(e);
                }
                newProvider.setManager(moduleManager);
                // 设置ModuleProvider与Module之间的关联关系
                // newProvider = StorageModuleElasticsearchProvider
                // this = StorageModule
                newProvider.setModuleDefine(this);
                // 将ModuleProvider对象记录到loadedProviders集合
                loadedProviders.add(newProvider);
            }
        }

        // 检查:该Module没有任何关联的ModuleProvider，会在这里报错
        if (!providerExist) {
            throw new ProviderNotFoundException(this.name() + " module no provider exists.");
        }

        for (ModuleProvider moduleProvider : loadedProviders) {
            logger.info("Prepare the {} provider in {} module.", moduleProvider.name(), this.name());
            try {
                // 前面读取配置信息时，ModuleProvider的配置信息是存储到ProviderConfig之中的Properties集合之中。
                // 此处，每个ModuleProvider都会关联一个ModuleConfig对象，并将ProviderConfig中的配置信息拷贝到
                // ModuleConfig对象中的相应字段，实现Properties到Java Bean的转换。
                copyProperties(
                        moduleProvider.createConfigBeanIfAbsent(),  // 创建 ModuleConfig 对象（关联的 ModuleConfig 对象）
                        configuration.getProviderConfiguration(moduleProvider.name()), // 获取 ProviderConfig 对象
                        this.name(),
                        moduleProvider.name()
                );
            } catch (IllegalAccessException e) {
                throw new ModuleConfigException(this.name() + " module config transport to config bean failure.", e);
            }
            // 调用ModuleProvider的prepare()方法，继续prepare流程
            moduleProvider.prepare();
        }
    }

    private void copyProperties(ModuleConfig dest, Properties src, String moduleName, String providerName) throws IllegalAccessException {
        if (dest == null) {
            return;
        }
        Enumeration<?> propertyNames = src.propertyNames();
        while (propertyNames.hasMoreElements()) {
            String propertyName = (String)propertyNames.nextElement();
            Class<? extends ModuleConfig> destClass = dest.getClass();

            try {
                Field field = getDeclaredField(destClass, propertyName);
                field.setAccessible(true);
                field.set(dest, src.get(propertyName));
            } catch (NoSuchFieldException e) {
                logger.warn(propertyName + " setting is not supported in " + providerName + " provider of " + moduleName + " module");
            }
        }
    }

    private Field getDeclaredField(Class<?> destClass, String fieldName) throws NoSuchFieldException {
        if (destClass != null) {
            Field[] fields = destClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.getName().equals(fieldName)) {
                    return field;
                }
            }
            return getDeclaredField(destClass.getSuperclass(), fieldName);
        }

        throw new NoSuchFieldException();
    }

    /**
     * @return providers of this module
     */
    final List<ModuleProvider> providers() {
        return loadedProviders;
    }

    // ModuleDefine.provider() 方法（继承自 ModuleProviderHolder 接口）不仅会返回底层的 ModuleProvider 实例，
    // 还会保证 loadedProviders 集合有且只有一个 ModuleProvider 实现存在。这也是为什么每个 Module 只会选用一个
    // ModuleProvider 实现的原因。
    @Override
    public final ModuleProvider provider() throws DuplicateProviderException, ProviderNotFoundException {
        if (loadedProviders.size() > 1) {
            throw new DuplicateProviderException(this.name() + " module exist " + loadedProviders.size() + " providers");
        } else if (loadedProviders.size() == 0) {
            throw new ProviderNotFoundException("There is no module provider in " + this.name() + " module!");
        }

        // 返回唯一的ModuleProvider实例
        return loadedProviders.getFirst();
    }
}
